using System;
using System.Collections;
using System.Xml;

namespace MediaWikiEngine
{
	/// <summary>
	/// Parses a bot script configuration.
	/// </summary>
	public class Config
	{
		XmlDocument _doc;

		ArrayList _commands = new ArrayList();

		public CommandDescriptor[] Commands
		{
			get { return (CommandDescriptor[]) _commands.ToArray(typeof(CommandDescriptor)); }
		}

		public Config(XmlDocument doc)
		{
			_doc = doc;

			Parse();
		}

		private void Parse()
		{
			foreach (XmlElement command in _doc.SelectNodes("/bot/command"))
			{
				CommandDescriptor cmd = new CommandDescriptor(command);
				_commands.Add(cmd);
			}
		}
	}

	public class CommandDescriptor
	{

		XmlElement _cmd;

		ArrayList _inputParams = new ArrayList();
		ArrayList _conditions = new ArrayList();
		ArrayList _outputParams = new ArrayList();

		public string CommandID;

		public InputParameter[] InputParameters
		{
			get { return (InputParameter[]) _inputParams.ToArray(typeof(InputParameter)); }
		}

		public OutputParameter[] OutputParameter
		{
			get { return (OutputParameter[]) _outputParams.ToArray(typeof(OutputParameter)); }
		}

		public Condition[] Conditions
		{
			get { return (Condition[]) _conditions.ToArray(typeof(Condition)); }
		}

		public CommandDescriptor(XmlElement cmd)
		{
			_cmd = cmd;

			Parse();
		}

		private void Parse()
		{
			CommandID = _cmd.GetAttribute("id");

			foreach (XmlElement input in _cmd.SelectNodes("input/in"))
			{
				InputParameter param = new InputParameter(input);

				_inputParams.Add(param);
			}

			foreach (XmlElement output in _cmd.SelectNodes("output/out"))
			{
				OutputParameter param = new OutputParameter(output);
				_outputParams.Add(param);
			}

			XmlElement conditions = _cmd.SelectSingleNode("conditions") as XmlElement;
			if (null != conditions)
			{
				foreach (XmlElement condition in conditions.ChildNodes)
				{
					switch (condition.Name)
					{
						case "if":
							IfCondition ifCond = new IfCondition(condition);
							_conditions.Add(ifCond);
							break;
						default:
							throw new ApplicationException("unknown condition type");
					}
				}
			}
		}
	}

	public abstract class Condition
	{
		protected XmlElement _cond;

		public string NextCommand;

		public Condition(XmlElement cond)
		{
			_cond = cond;
		
			Parse();
		}

		public abstract void Parse();

		public abstract bool Evaluate(Hashtable globalParams, Hashtable localParams);
	}

	public class IfCondition : Condition
	{
		public IfCondition(XmlElement cond) : base(cond) { }

		public string Value;
		public string Relation;
		public string Test;
		public string FalseCommandName;

		public override void Parse()
		{
			Value = _cond.GetAttribute("value");
			Relation = _cond.GetAttribute("relation");
			Test = _cond.GetAttribute("test");
			FalseCommandName = _cond.GetAttribute("false");
		}

		public override bool Evaluate(Hashtable globalParams, Hashtable localParams)
		{
			NextCommand = FalseCommandName;

			switch (Relation.ToLower())
			{
				case "isset":
					if (true == Value.StartsWith("$$"))
					{
						object val = globalParams[Value];
						if (null != val)
							return true;
					}
					if (true == Value.StartsWith("$"))
					{
						object val = localParams[Value];
						if (null != val)
							return true;
					}

					return false;
					
				case "equals":
					if (true == Value.StartsWith("$$"))
					{
						object val = globalParams[Value];
						if (val.ToString() == Test)
							return true;
					}
					if (true == Value.StartsWith("$"))
					{
						object val = localParams[Value];
						if (val.ToString() == Test)
							return true;
					}
					else
					{
						object val = Value;
						if (val.ToString() == Test)
							return true;
					}

					return false;
			}

			return false;
		}

	}


	public class OutputParameter
	{
		public string Name;
		public string Target;

		XmlElement _param;

		public OutputParameter(XmlElement param)
		{
			_param = param;

			Parse();
		}

		private void Parse()
		{
			Name = _param.GetAttribute("name");
			Target = _param.GetAttribute("target");
		}
	}

	public class InputParameter
	{
		public string Name;
		public string Value;

		XmlElement _param;

		public InputParameter(XmlElement param)
		{
			_param = param;

			Parse();
		}

		private void Parse()
		{
			Name = _param.GetAttribute("name");

			if (true == _param.HasAttribute("value"))
				Value = _param.GetAttribute("value");
			else
				Value = _param.InnerText;
		}
	}

}
